/**
 * Promise:异步解决方案；
 *  1.promise是一个类，执行这个类的时候要传递一个执行器，执行器会立即执行；
 *  2.promise具有三个状态：
 *      等待中 pending; 成功的 successful; 失败的 unsuccessful;
 *  3.promise的状态确定后不能更改，只能从发送转为成功或者失败；
 *  4.promise具有两个改变状态的方法 当方法执行之后状态改变 且无法再次更改状态:
 *      reject 失败；resolve 成功；
 *  5.promise具有then方法,可以判断状态值,成功即调用成功的回调 失败调用失败的回调;
 *      then的回调: 成功回调 successCallBack, 失败回调 unsuccessCallBack
 *  6.successCallBack需要返回成功之后的值, unsuccessCallBack要将错误原因传输;
 *      _successResult 成功的结果, _errorMessage 错误原因
 *  7.promise支持异步处理.当status没有变化时,将成功的回调与失败的回调与执行结果\错误原因的值保存起来;
 * 并在状态改变的时候触发回调执行;
 *      _success 存储成功回调 _unsuccess 存储失败回调
 *  8.then方法多次调用可以添加多个回调函数 所以存储失败回调需要是数组类型;
 *  9.then方法可以链式调用，需要在then方法中new Promise 并且传递执行器进去,
 * 并且要将上一个then方法的返回值给下一个then；
 *  10.链式调用需要判断返回的是普通值还是promise对象，可以封装一个公共的方法用于判断对象 并将所需返回值返回；
 *  11.promise能识别到自己返回自己的时候报错；
 *      所以要用then中实例化的promise对象和即将返回的回调函数的返回值对比 相等的时候要调用reject；
 *      因为then的实例对象在实例化过程并未完全创建所以要将then的实例对象内的代码进行异步处理；
 *  12.promise 运行过程中要捕获其异常；
 *  13.promise包含静态方法all 此方法可以使异步函数调用顺序得到结果；
 *  14.promise包含静态方法resolve 此方法可以将方法包装成promise对象；
 *  15.promise包含finally,无论promise的状态返回成功与否 都要执行一次;
 *  16.promise包含catch,收集错误 执行发生错误时的callback;
 */

// promise的状态值
const PENDING = 'pending';
const SUCCESSFUL = 'successful';
const UNSUCCESSFUL = 'unsuccessful';

class MyPromise {
    constructor(executor) {
        // 调用执行器需要传递reject、resolve
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error)
        }
    }
    // status用来存储这个变量
    status = PENDING;
    // 成功的值
    _successResult = undefined;
    // 失败的值
    _errorMessage = undefined;
    // 成功的回调函数
    _success = []
    // 失败的回调函数
    _unsuccess = []
    // resolve 方法,改变状态到成功且不能更改到失败
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = SUCCESSFUL;
        // 将成功的值赋给_successResult
        this._successResult = value;
        // 判断成功的回调是否存在,如果存在即状态变更为成功,将执行结果返回
        // 使用循环执行数组内的每一个回调
        while (this._success.length) this._success.shift()();
    }
    // reject 方法, 改变状态到失败且不能更改到成功
    reject = message => {
        if (this.status !== PENDING) return;
        this.status = UNSUCCESSFUL;
        // 返回失败信息_errorMessage
        this._errorMessage = message;
        // 判断失败的回调是否存在,如果存在即状态变更为失败,将错误信息返回
        while (this._unsuccess.length) {
            this._unsuccess.shift()(this._errorMessage)
        }
    }
    // then方法
    then(successCallBack, unsuccessCallBack) {
        // 参数可选
        successCallBack = successCallBack ? successCallBack : value => value;
        unsuccessCallBack = unsuccessCallBack ? unsuccessCallBack : reason => { throw reason };
        let thenPromise = new MyPromise((resolve, reject) => {
            // 为了让下一个then方法接受到上一个then方法的返回值，要将回调函数的返回值作为参数，传递给resolve\reject
            // 使用setTimeOut使内部代码异步
            switch (this.status) {
                case SUCCESSFUL:
                    setTimeout(() => {
                        try {
                            // 将结果作为参数传递
                            let thenResolve = successCallBack(this._successResult);
                            resPromise(thenPromise, thenResolve, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                    break;
                case UNSUCCESSFUL:
                    // 将结果作为参数传递
                    setTimeout(() => {
                        try {
                            unsuccessCallBack(this._errorMessage)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                    break;
                default:
                    // 存在异步代码的时候将成功与失败的回调函数存储起来
                    // 判断 x 的值是普通值还是promise对象
                    // 如果是普通值 直接调用resolve 
                    // 如果是promise对象 查看promsie对象返回的结果 
                    // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                    this._success.push(() => {
                        setTimeout(() => {
                            try {
                                let x = successCallBack(this._successResult);
                                resPromise(thenPromise, x, resolve, reject)
                            } catch (e) {
                                reject(e);
                            }
                        }, 0)
                    });
                    this._unsuccess.push(unsuccessCallBack);
                    break;
            }
        });
        return thenPromise;
    }
    // finally方法 不论状态成功都要执行要执行一次
    // 返回的结果要等待异步执行完毕 使用resolve处理
    finally(callBack) {
        this.then(value => {
            return MyPromise.resolve(callBack()).then(() => value);
        }, err => {
            return MyPromise.resolve(callBack()).then(() => { throw err });
        })
    }
    // catch方法
    catch(unsuccessCallBack) {
        return this.then(undefined, unsuccessCallBack)
    }
    // all方法 参数使数组 返回promise对象 将执行结果以数组返回
    static all(arr) {
        // 用于返回结果的数组
        let result = [];
        // 长度标志位 当此标志位和数组长度相同时 任务结束 返回结果
        let index = 0;
        // 返回的promise对象
        return new MyPromise((resolve, reject) => {
            // 此方法可将返回结果放入数组
            function pushInResult(key, value) {
                index++;
                result[key] = value;
                index === arr.length ? resolve(result) : '';
            }
            for (let i = 0; i < arr.length; i++) {
                const element = arr[i];
                // 判断是否为promise对象
                // 普通值直接返回
                // 为promise对象要将then的结果放入数组
                switch (element instanceof MyPromise) {
                    case true:
                        // promise对象
                        element.then(value => resolve(i, value), err => reject(err))
                        break;
                    case false:
                        // 普通值
                        pushInResult(i, arr[i])
                        break;
                    default:
                        reject(new TypeError('Nothing'))
                        break;
                }
            }
        });
    }
    // resolve方法
    static resolve(value) {
        // 判断是否为普通值,是普通值 直接返回不是普通值要包装成promise对象
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value))
    }

}

function resPromise(thenPromiseObj, res, resolve, reject) {
    // 从这里比较then的Promise对象与成功的回调是否相等
    if (thenPromiseObj === res) return reject(new TypeError('你不能自己返回自己'))
    switch (res instanceof MyPromise) {
        case true:
            // 是promise对象就使用then方法 并将结果作为参数传递
            res.then(value => resolve(value), reason => reject(reason))
            break;
        case false:
            // 是普通值 直接返回
            resolve(res)
            break;
        default:
            new Error('Nothing');
            break;
    }
}

module.exports = MyPromise